﻿/*
 * Created by SharpDevelop.
 * User: Administrator
 * Date: 2016/4/21
 * Time: 17:42
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using SharpChannel.Core;
using SharpChannel.Core.Packet;
using SharpChannel.Rpc.Proxy;
using SharpChannel.Rpc.Serialization;
using log4net;
using SharpChannel.Util;
using System.Linq;

namespace SharpChannel.Rpc
{
    /// <summary>
    /// Description of SharpChannelServer.
    /// </summary>
    public class SharpChannelServer<T> where T : class
    {
        private static ILog log = LoggerHelper.GetLogger();

        private readonly TcpServer server;

        private readonly T proxyObject;

        private readonly Dictionary<string, MethodInfo> name2MethodInfo = new Dictionary<string, MethodInfo>();

        private readonly ThreadLocal<ISerializable> serialization = new ThreadLocal<ISerializable>(() => FormatterManager.GetInstance().GetSerializable());

        private readonly ThreadLocal<BaseSession> currentSessions = new ThreadLocal<BaseSession>();

        public TcpServer.SessionStateChanged OnSessionStageChanged { get { return server?.OnSessionStateChanged; } set { if(server != null) server.OnSessionStateChanged = value; } }

        public BaseSession GetCurrentSession()
        {
            return currentSessions.Value;
        }

        private void server_NewPacketReceived(object sender, NewPacketArgs args)
        {
            var data = args.Data;
            var obj = serialization.Value.Deserialize(data);
            if (obj is MethodInvokeInfo)
            {
                var methodInfo = obj as MethodInvokeInfo;
                var methodName = methodInfo.MethodFullName;
                if (name2MethodInfo.ContainsKey(methodName))
                {
                    currentSessions.Value = args.Session;
                    var methodDefine = name2MethodInfo[methodName];
                    object result = null;
                    if (methodDefine.IsGenericMethod)
                    {
                        result = methodDefine.MakeGenericMethod(methodInfo.Args[0].GetType().GetGenericArguments()).Invoke(proxyObject, methodInfo.Args);
                    }
                    else
                    {
                        result = name2MethodInfo[methodName].Invoke(proxyObject, methodInfo.Args);
                    }
                    methodInfo.Args = null;
                    methodInfo.ReturnValue = result;
                    currentSessions.Value = null;
                    args.Session.Send(serialization.Value.Serialize(methodInfo));
                }
                else
                {
                    LoggerHelper.GetLogger().Error("server proxy object has no method named:" + methodName);
                }

            }
            else
            {
                LoggerHelper.GetLogger().Error("packet cann't deserialize");
            }
        }

        public SharpChannelServer(T obj)
        {
            proxyObject = obj;
            foreach (var info in proxyObject.GetType().GetMethods())
            {
                log.Info("register method:" + info.Name);
                name2MethodInfo[info.Name] = info;
            }
            server = new TcpServer();
            server.NewPacketReceived += server_NewPacketReceived;
            server.Start();
        }

        public void CloseChannel()
        {
            try
            {
                server.Stop();
            }
            catch (Exception ex)
            {
                LoggerHelper.GetLogger().Error("stop server exception", ex);
            }
        }
    }
}
